﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// NotReviewedButNeeded
#pragma once

#include "NdbObjectContainer.h"
#include "NdbObjectArchiveHelper.h"
#include "NdbPropertySerialize.h"
#include "NdbReplaceElementIdHelper.h"
#include "NdbReportElementIdHelper.h"
#include "NdbCopyHelper.h"
#include "NdbObjectMemoryModelGroupNode.h"
#include "NdbArchiveBlockHeader.h"
#include <vector>

namespace gcmp
{
    class IDbObjectArchive;
    template <typename T>
    class NdbVector : public NdbObjectContainer
    {
        typedef std::true_type is_vector;
    public:
        NdbVector()
        {
        }

        NdbVector(const NdbVector& src)
            : m_data(src.m_data)
        {
        }

        NdbVector(const std::vector<T>& data)
        {
            m_data = data;
        }

        NdbVector& operator=(const NdbVector& src)
        {
            if (this != &src)
            {
                m_data = src.m_data;
            }
            
            return *this;
        }

        NdbVector& operator=(const std::vector<T>& data)
        {
            if (&m_data != &data)
            {
                m_data = data;
            }

            return *this;
        }

        NdbVector& operator = (NdbVector&& another)
        {
            if (this != &another)
            {
                m_data = std::move(another.m_data);
            }
            return *this;
        }

        NdbVector& operator=(std::vector<T>&& data)
        {
            if (&m_data != &data)
            {
                m_data = std::move(data);
            }

            return *this;
        }

        virtual ~NdbVector()
        {

        }

        operator const std::vector<T>& () const 
        {
            return m_data;
        }

        operator std::vector<T>& () 
        {
            return m_data;
        }

    public:
        virtual NdbPropertySchemaType GetSchemaType() const override
        {
            return NdbPropertySchemaType::Vector;
        }

        virtual gcmp::OwnerPtr<NdbObjectContainer> NdbClone()const override
        {
            OwnerPtr<NdbVector<T>> opCloneVector = NEW_AS_OWNER_PTR(NdbVector<T>);
            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                T k = NdbCopyHelper<T>::Copy(*ite);
                opCloneVector->push_back(std::move(k));
            }
            return TransferOwnership(opCloneVector); 
        }

        virtual void NdbAssignByMove(gcmp::OwnerPtr<NdbObjectContainer> opSrc) override
        {
            NdbVector<T> * pVec = dynamic_cast<NdbVector<T>*>(opSrc.get());
            if (pVec)
            {
                *this = std::move(*pVec);
            }
        }

        virtual UInt64 NdbSerialize(IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter) const override
        {
            UInt64 nSize = size();
            NdbContainerArchiveBlockHeader containerArchiveBlockHeader(NdbContainerArchiveBlockHeader::s_CurrentVersion, nSize, NdbPropertySchemaType::Vector);
            if (property_is_value_type<T>::value)
            {
                NdbArchiveBlockHeader blockHeader = GetSharedBlockHeader<T>();
                containerArchiveBlockHeader.SetSharedHeader(blockHeader, 0);
                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());

                for (int i = 0; i < (int)nSize; ++i)
                {
                    length += NdbPropertySerializeWithoutHeader<T>::Serialize(m_data[i], objectArchive, adapter);
                }
                return length;
            }
            else
            {
                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());
                for (int i = 0; i < (int)nSize; ++i)
                {
                    length += NdbPropertySerialize<T>::Serialize(m_data[i], objectArchive, adapter);
                }
                return length;
            }
        }

        virtual void NdbExtractObjectFromMemory(NdbObjectMemoryModelNode * pObjMemModel, UInt32 majorVersion) override
        {
            NdbObjectMemoryModelGroupNode *pMemGroupNode = thread_safe_quick_cast<NdbObjectMemoryModelGroupNode>(pObjMemModel);
            DBG_WARN_AND_RETURN_VOID_UNLESS(pMemGroupNode, L"pMemGroupNode为空？", L"GMEP", L"2020/11/24");
            std::vector<OwnerPtr<NdbObjectMemoryModelNode>>& subNodes = pMemGroupNode->GetSubNodesFW();
            UInt32 nSize = (UInt32)subNodes.size();
            m_data.resize(nSize);
            for (UInt32 i = 0; i < nSize; ++i)
            {
                NdbObjectMemoryModelNode *pSubNode = subNodes[i].get();
                if (!pSubNode) continue;
                switch (pSubNode->GetSchemaType())
                {
                case NdbPropertySchemaType::Value:
                    NdbObjectArchiveHelper::GetValueFromMemoryModel(&m_data[i], pSubNode);
                    break;
                case NdbPropertySchemaType::ElementId:
                    NdbObjectArchiveHelper::GetElementIdFromMemoryModel(&m_data[i], pSubNode);
                    break;
                case NdbPropertySchemaType::WString:
                    NdbObjectArchiveHelper::GetWStringFromMemoryModel(&m_data[i], pSubNode);
                    break;
                case NdbPropertySchemaType::SchematicObject:
                    NdbObjectArchiveHelper::GetSchematicObjectFromMemoryModel(&m_data[i], pSubNode, false/*isParentSubObject*/, majorVersion);
                    break;
                case NdbPropertySchemaType::CloneableObject:
                    NdbObjectArchiveHelper::GetCloneableObjectFromMemoryModel(&m_data[i], pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToNdbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrFromMemoryModel(&m_data[i], pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToIDbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrToIDbObjectFromMemoryModel(&m_data[i], pSubNode,majorVersion);
                    break;
                case NdbPropertySchemaType::NativePointer:
                    NdbObjectArchiveHelper::GetPointerFromMemoryModel(&m_data[i], pSubNode);
                    break;
                default:
                    DBG_WARN(L"NdbVector: 不支持存放的类型" + NdbPropertyUtils::SchemaTypeAsWString(pSubNode->GetSchemaType()), L"GMEP", L"2017-01-16");
                    break;
                }
            }
        }

        virtual bool HasElementIdProperty() const override
        {
            if (m_data.empty()) return false;

            // T的类型是OwnerPtr时，不能通过第一个元素确定。详见ModelIdDetectorHelper的OwnerPtr特化版本说明
            auto ite = m_data.begin();
            return ElementIdDetectorHelper<T>::DetectElementId(&(*ite));
        }

        virtual void ReplaceElementIds(const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap) override
        {
            if (!HasElementIdProperty()) return;

            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                ReplaceElementIdHelper<T>::ReplaceElementId(&(*ite), oldToNewMap);
            }
        }

        virtual void ReportElementIds(
            std::set<ElementId>* pModelIds,
            std::unordered_map<ElementId, std::vector<std::wstring>, ElementIdHash>* pPaths,
            std::vector<std::wstring>* pCurrentPath
        ) const override
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(pModelIds, L"pModelIds不应该为空", L"GMEP", L"2016/10/14");
            if (!HasElementIdProperty()) return;

            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                ReportElementIdHelper<T>::ReportElementId(*ite, pModelIds, pPaths, pCurrentPath);
            }
        }

        virtual void CollectModuleGroupsOfNdbObject(std::set<Guid, GuidComparer>& moduleGroupsGuids) const
        {
            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                NdbPropertySerialize<T>::CollectModuleGroups(*ite, moduleGroupsGuids);
            }
        }

        virtual void CheckTopOwnerObject(std::vector<const UniqueWString*>& path,const NdbObject* correctowner,
            const NdbClassSchema* pNdbClassSchema, std::vector<std::wstring>& resultstr) const override
        {
            const std::wstring& subpropertyname = UniqueWString::Find(L"")->GetContent();
            std::vector<const UniqueWString*> localpath;
            localpath.insert(localpath.end(), path.begin(), path.end());
            localpath.push_back(UniqueWString::Find(L""));

            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                if (!std::is_base_of<IDbObject, T>::value)
                {
                    auto result = Convert_Type_If<std::is_base_of<NdbObject, T>::value, property_is_owner_ptr_type<T>::value, NdbObject, T>::GetData((*ite));
                    NdbObject* pObject = GetNonConstPoint<NdbObject*>::GetNonConstPointImp(result);

                    if (pObject != nullptr)
                    {
                        pObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }
                else
                {
                    auto result = Convert_Type_If<std::is_base_of<IDbObject, T>::value, property_is_owner_ptr_type<T>::value, IDbObject, T>::GetData((*ite));
                    IDbObject* pPropertyIDbObject = GetNonConstPoint<IDbObject*>::GetNonConstPointImp(result);
                    if (pPropertyIDbObject != nullptr)
                    {
                        NdbObject* pPropertyNDbObject = dynamic_cast<NdbObject*>(pPropertyIDbObject);
                        if (pPropertyNDbObject == nullptr)
                            continue;

                        //FYI(L"log test:"+subpropertyname)
                        pPropertyNDbObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);
                    }

                }
            }

        }

    public:
        size_t size() const { return m_data.size(); }

        void push_back(T&& val){m_data.push_back(std::forward<T>(val));}
        void push_back(const T& val){m_data.push_back(val);}
       
        void clear(){m_data.clear();}

        T& operator[](size_t i){ return m_data[i];}
        const T& operator[](size_t i)const{return m_data[i];}

        const std::vector<T>& StdVector() const { return m_data; }
        std::vector<T>& StdVectorFW() { return m_data; }
    private:
        std::vector<T> m_data;
    };
}
